using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Axe.Logging;
using System.Diagnostics;

namespace Axe.Workflow
{
    public sealed class ThreadTaskParams
    {
        private bool m_MakeThreadBackground = false;

        public bool MakeThreadBackground
        {
            get { return m_MakeThreadBackground; }
            set { m_MakeThreadBackground = value; }
        }
        private ThreadStart m_StartRountine;
        private ThreadPriority m_Priority;

        public ThreadTaskParams (ThreadStart startRountine, ThreadPriority priority)
        {
            StartRountine = startRountine;
            Priority = priority;
        }

        public ThreadTaskParams(ThreadStart startRountine, ThreadPriority priority, bool makeBG)
        {
            StartRountine = startRountine;
            Priority = priority;
            MakeThreadBackground = makeBG;
        }

        public ThreadPriority Priority
        {
            get { return m_Priority; }
            set { m_Priority = value; }
        }

        public ThreadStart StartRountine
        {
            get { return m_StartRountine; }
            set { m_StartRountine = value; }
        }
    }
    
    public class ThreadTask : Task, IDisposable
    {
        ManualResetEvent MRE = new ManualResetEvent(false);
        List<Pair<Thread, ThreadTaskParams>> Threads = new List<Pair<Thread, ThreadTaskParams>>();

        private void ThreadLoop(object Obj)
        {
            try
            {
                ThreadTaskParams Params = (ThreadTaskParams)Obj;

                if (Params.MakeThreadBackground)
                    Thread.CurrentThread.IsBackground = true;

                ThreadStart Method = Params.StartRountine;

                while (IsRunning || IsPaused)
                {
                    MRE.WaitOne();
                    Method();
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(String.Format("Caught exception in thread {0}:", 
                    Thread.CurrentThread.Name ?? Thread.CurrentThread.ManagedThreadId.ToString()));
                Debug.WriteLine(e.Message);
                throw e;
            }
            finally
            {
                Debug.WriteLine("Thread exited");
            }
        }

        #region public void AddParallelTask(ThreadStart action, bool makeBackground)
        /// <summary>
        /// Adds delegate that runs in the parallel thread.
        /// </summary>
        /// <mParam name="ThreadLoopDelegate">The thread loop delegate.</mParam>
        public void AddParallelTask(ThreadStart action, bool makeBackground)
        {
            AddParallelTask(action, ThreadPriority.Normal, makeBackground);
        }
        #endregion

        #region public void AddParallelTask(ThreadStart action, ThreadPriority priority, bool makeBackground)
        public void AddParallelTask(ThreadStart action, ThreadPriority priority, bool makeBackground)
        {
            lock (Threads)
            {
                ThreadTaskParams p = new ThreadTaskParams(action, priority, makeBackground);
                Threads.Add(new Pair<Thread, ThreadTaskParams>(null, p));
            }
        } 
        #endregion


        #region public void ClearTaskList()
        public void ClearTaskList()
        {
            TimeSpan joinTimeout = TimeSpan.FromSeconds(15);

            lock (Threads)
            {
                foreach (Pair<Thread, ThreadTaskParams> thread in Threads)
                {
                    if (thread.Left != null)
                    {
                        thread.Left.Join(joinTimeout);
                    }
                }
                Threads.Clear();
            }
        } 
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadTask"/> class.
        /// </summary>
        public ThreadTask() : base()
        {
            OnStarting += new ExecutionStateChanging(OnTaskStarting);
            OnStopping += new ExecutionStateChanging(OnTaskStopping);

            OnResuming += new ExecutionStateChanging(OnTaskResuming);
            OnSuspending += new ExecutionStateChanging(OnTaskSuspending);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadTask"/> class.
        /// </summary>
        public ThreadTask(string taskName)
            : base(taskName)
        {
            OnStarting += new ExecutionStateChanging(OnTaskStarting);
            OnStopping += new ExecutionStateChanging(OnTaskStopping);
            
            OnResuming += new ExecutionStateChanging(OnTaskResuming);
            OnSuspending += new ExecutionStateChanging(OnTaskSuspending);
        }

        bool OnTaskSuspending()
        {
            MRE.Reset();
            return true;
        }

        bool OnTaskResuming()
        {
            MRE.Set();
            return true;
        }

        bool OnTaskStopping()
        {
            MRE.Set();

            lock(Threads)
            {
                foreach(Pair<Thread, ThreadTaskParams> Item in Threads)
                {
                    if(Item.Left != null && Item.Left.ThreadState != System.Threading.ThreadState.Stopped)
                    {
                        Item.Left.Join();
                        Item.Left = null;
                    }
                }
            }

            return true;
        }

        bool OnTaskStarting()
        {
            MRE.Set();

            lock(Threads)
            {
                foreach(Pair<Thread, ThreadTaskParams> Item in Threads)
                {
                    if(Item.Left == null || Item.Left.ThreadState == System.Threading.ThreadState.Stopped)
                    {
                        Thread T = new Thread(new ParameterizedThreadStart(ThreadLoop));
                        T.Priority = Item.Right.Priority;
                        T.Name = String.Format("Thread Owner: {0}/{1} Thread ID:{2}", GetType().Name, GetHashCode(), T.GetHashCode());
                        T.SetApartmentState(ApartmentState.MTA);
                        
                        Item.Left = T;
                    }

                    Item.Left.Start(Item.Right);
                }
            }

            return true;
        }

        #region IDisposable Members

        public virtual void Dispose()
        {
            Stop();
            MRE.Close();
        }

        #endregion
    };
}
